package cn.com.open.itembank.service.impl;

import cn.com.open.itembank.common.*;
import cn.com.open.itembank.common.enums.*;
import cn.com.open.itembank.dto.*;
import cn.com.open.itembank.entity.*;
import cn.com.open.itembank.exception.PoiException;
import cn.com.open.itembank.mapper.QuestionReadMapper;
import cn.com.open.itembank.mapper.QuestionWriteMapper;
import cn.com.open.itembank.redis.RedisConfig;
import cn.com.open.itembank.service.*;
import cn.com.open.itembank.util.FileUtils;
import cn.com.open.itembank.utils.*;
import com.alibaba.fastjson.JSONObject;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import org.apache.commons.collections.map.HashedMap;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * Created by lucky on 2017/9/1.
 */
@Service
public class ItemServiceImpl implements ItemService {

    private static org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(ItemServiceImpl.class);

    @Autowired
    private QuestionReadMapper readMapper;
    @Autowired
    private QuestionWriteMapper writeMapper;
    @Autowired
    private ItemBankRemoteService itemBankService;
    @Autowired
    private QuestionTypeRemoteService questionTypeService;
    @Autowired
    private QuestionCategoryRemoteService questionCategoryService;
    @Autowired
    private QtiRemoteService qtiService;
    @Autowired
    private RedisConfig redis;

    private static String itembankIdNotExist = "题库不存在";
    private static String modelCheckFailed = "数据模型校验失败";
    private static String noPermission = "无权操作试题";
    private static String countsString = "count";
    private static String rowsString = "rows";
    private static String resultsString = "result";
    private static String exportString = "export";
    private static int fontSize = 11;
    private static String fontFamily = "宋体";
    private static String redisItemKey = "itembank:question:";

    //region 查询

    //region 判断题库和试题是否存在关系

    /**
     * 判断题库和试题是否存在关系.
     *
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the boolean
     */
    @Override
    public boolean checkItemBankQuestionRelation(String itemBankId, String id) {
        if (StringUtils.isEmpty(itemBankId) || StringUtils.isEmpty(id)) {
            return false;
        }

        QuestionUseRelation relation = readMapper.getQuestionUseRelation(itemBankId, id);

        return (relation != null);
    }
    //endregion

    //region 获取试题基本信息

    /**
     * 获取试题基本信息.
     *
     * @param id 试题id
     * @return the item
     */
    @Override
    public Question getItem(String id) {

        if (StringUtil.isEmpty(id)) {
            return null;
        }

        String key = String.format("%s%s", redisItemKey, id);
        if (redis.isKeyExists(key)) {
            return redis.getObject(key, Question.class);
        }

        Question result = readMapper.getById(id);
        redis.set(key, result, 3600);

        return result;
    }
    //endregion

    //region 获得指定题库下指定试题的试题模型

    /**
     * 获得指定题库下指定试题的试题模型
     *
     * @param itemBankId 题库id
     * @param id         试题id
     * @param type       获取答案类型：0，全部；1，试题；2，答案
     * @return 试题模型 return model
     */
    @Override
    public ReturnModel getItemBankQuestion(String itemBankId, String id, AnswerType type) {
        ReturnModel model = getItemBankQuestionBasic(itemBankId, id, type == AnswerType.NOANSWER ? false : true);
        QuestionModel question = (QuestionModel) model.getData();

        //如果仅获取答案则使用map方式返回
        if (type == AnswerType.ANSWER) {
            Map<String, Object> map = new HashedMap();

            if (question != null) {
                switch (ItemType.fromValue(question.getI3())) {
                    case CLOZE:
                    case COMPOSITE:
                    case READINGCOMPREHENSION:
                        List<Map<String, Object>> sub = new ArrayList<>();
                        if (question.getSub() != null) {
                            for (QuestionModel subItem : question.getSub()) {
                                Map<String, Object> subMap = new HashedMap();
                                subMap.put("I1", subItem.getI1());
                                subMap.put("I7", subItem.getI7());
                                subMap.put("I10", subItem.getI10());
                                sub.add(subMap);
                            }
                        }
                        map.put("I1", question.getI1());
                        map.put("I10", question.getI10());
                        map.put("Sub", sub);
                        break;
                    default:
                        map.put("I1", question.getI1());
                        map.put("I7", question.getI7());
                        map.put("I10", question.getI10());
                        break;
                }
            }

            model.setData(map);
        }

        return model;
    }

    /**
     * 获得指定题库下指定试题的试题模型(paper用).
     *
     * @param itemBankId    题库id
     * @param id            试题id
     * @param includeAnswer 是否包含答案
     * @return 试题模型 return model
     */
    @Override
    public QuestionModel getItemBankQuestionForPaper(String itemBankId, String id, boolean includeAnswer) {

        ReturnModel model = getItemBankQuestionBasic(itemBankId, id, includeAnswer);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return null;
        }
        return (QuestionModel) model.getData();
    }

    /**
     * 获得指定题库下指定试题的试题模型(内部调用).
     *
     * @param itemBankId    题库id
     * @param id            试题id
     * @param includeAnswer 是否包含答案
     * @return 试题模型 return model
     */
    private ReturnModel getItemBankQuestionBasic(String itemBankId, String id, boolean includeAnswer) {
        ReturnModel model = new ReturnModel();

        if (StringUtils.isEmpty(itemBankId) || StringUtils.isEmpty(id)) {
            model.setCode(GlobalConst.INCOMPLETE_DATA);
            model.setMessage("参数不完整，缺少题库ID或者试题ID");
            return model;
        }

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        ItemDto item = getItemInfo(id);
        if (item == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage("试题不存在");
            return model;
        }

        //获取试题关系，用于更新试题模型中状态
        QuestionUseRelation relation = readMapper.getQuestionUseRelation(itemBankId, id);

        if (relation == null) {
            model.setCode(GlobalConst.QUESTION_ITEM_BANK_NOT_MATCH);
            model.setMessage("该题库下无指定试题信息");
            return model;
        }

        QuestionChapterInfo chapterInfo = readMapper.getQuestionChapter(itemBankId, id);

        QuestionModel question = getQuestionModel(item, itemBank, chapterInfo, includeAnswer);
        if (question == null) {
            model.setCode(GlobalConst.OTHER_ERROR);
            model.setMessage("试题转换错误");
            return model;
        }

        //试题的状态以关系表的状态为准
        question.setI35(relation.getStatus());

        model.setCode(GlobalConst.SUCCESS);
        model.setData(question);

        return model;
    }
    //endregion

    //region 获取试题应用类别信息

    /**
     * 根据试题ID查询试题应用类别信息.
     *
     * @param id 试题ID
     * @return 试题应用类别信息
     */
    @Override
    public ReturnModel getQuestionCategory(String id) {
        ReturnModel model = new ReturnModel();
        model.setCode(GlobalConst.SUCCESS);
        model.setData(readMapper.getQuestionCategorys(id));

        return model;
    }
    //endregion

    //region 随机获取指定题库指定题型指定数量的试题集合

    /**
     * 随机获取指定题库指定题型指定数量的试题集合.
     *
     * @param itemBankId     题库id
     * @param questionTypeId 题型id
     * @param cnt            获取数量
     * @return 试题id集合
     */
    @Override
    public ReturnModel getRandomQuestionsByItemBankIdAndQType(String itemBankId, String questionTypeId, int cnt) {
        ReturnModel model = new ReturnModel();

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        List<QuestionModel> questions = new ArrayList<>();
        List<String> questionIds = readMapper.getRandomQuestionsByItemBankIdAndQType(itemBankId, questionTypeId, cnt);

        for (String id : questionIds) {
            ReturnModel idResult = getItemBankQuestion(itemBankId, id, AnswerType.ALL);
            if (idResult.getCode() == GlobalConst.SUCCESS) {
                questions.add((QuestionModel) idResult.getData());
            }
        }

        model.setData(questions);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 根据题库id获取指定题库下试题按照题型，难度，应用类别的数量统计列表（OES入学测试用）

    /**
     * 根据题库id获取指定题库下试题按照题型，难度，应用类别的数量统计列表.
     *
     * @param itemBankId 题库id
     * @return 数量统计列表
     */
    @Override
    public ReturnModel getQuestionStatByItemBankId(String itemBankId) {
        ReturnModel model = new ReturnModel();

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        List<QuestionStateModel> list = readMapper.getQuestionStatByItemBankId(itemBankId);
        model.setCode(GlobalConst.SUCCESS);
        model.setData(list);
        return model;
    }
    //endregion

    //region 根据题库id获取指定题库下试题按照应用类别的数量统计列表（mooc用，与3.0不同）

    /**
     * 根据题库id获取指定题库下试题按照应用类别的数量统计列表.
     *
     * @param itemBankId 题库id
     * @return 数量统计列表
     */
    @Override
    public ReturnModel getQuestionStatForCategoryByItemBankId(String itemBankId) {
        ReturnModel model = new ReturnModel();

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        List<QuestionCategoryStateModel> list = readMapper.getQuestionStatForCategoryByItemBankId(itemBankId);
        model.setCode(GlobalConst.SUCCESS);
        model.setData(list);
        return model;
    }
    //endregion

    //region 根据试题数量查询模型获取指定条件下试题数量（策略用）

    /**
     * 根据试题数量查询模型获取指定条件下试题数量（策略用）.
     *
     * @param filterModels the filter model
     * @return the question count for rules check
     */
    @Override
    public Map<String, Integer> getQuestionCountForRulesCheck(Map<String, QuestionCountFilterModel> filterModels) {

        Map<String, Integer> resultMap = new LinkedHashMap<>();

        //循环所有筛选模型
        for (Map.Entry<String, QuestionCountFilterModel> map : filterModels.entrySet()) {
            resultMap.put(map.getKey(), getRuleCheckCount(map.getValue()));
        }

        return resultMap;
    }

    /**
     * 根据试题数量查询模型获取指定条件下试题数量.
     *
     * @param filterModel the filter model
     * @return the rule check count
     */
    private int getRuleCheckCount(QuestionCountFilterModel filterModel) {
        int result = checkQuestionCountFilterModel(filterModel);

        if (result == GlobalConst.SUCCESS) {
            //查询数量
            return readMapper.getQuestionCountForRulesCheck(filterModel, true);
        }

        return result;
    }
    //endregion

    //region 根据试题数量查询模型获取指定条件下试题（策略组卷用）

    /**
     * 根据试题数量查询模型获取指定条件下试题（策略组卷用）.
     *
     * @param filterModels the filter model
     * @return the question count for rules check
     */
    @Override
    public Map<String, List<RuleQuestionModel>> getQuestionForRule(Map<String, QuestionCountFilterModel> filterModels) {

        Map<String, List<RuleQuestionModel>> resultMap = new LinkedHashMap<>();

        //循环所有筛选模型
        for (Map.Entry<String, QuestionCountFilterModel> map : filterModels.entrySet()) {
            resultMap.put(map.getKey(), getRuleCheckList(map.getValue()));
        }

        return resultMap;
    }

    /**
     * 根据试题数量查询模型获取指定条件下试题.
     *
     * @param filterModel the filter model
     * @return the rule check count
     */
    private List<RuleQuestionModel> getRuleCheckList(QuestionCountFilterModel filterModel) {
        if (checkQuestionCountFilterModel(filterModel) == GlobalConst.SUCCESS) {
            //查询数量
            return readMapper.getQuestionForRules(filterModel, false);
        }

        return new ArrayList<>();
    }

    /**
     * 检查试题数量查询模型.
     *
     * @param filterModel the filter model
     * @return the int
     */
    private int checkQuestionCountFilterModel(QuestionCountFilterModel filterModel) {
        if (StringUtils.isEmpty(filterModel.getItemBankId()) || StringUtils.isEmpty(filterModel.getQuestionTypeID())) {
            return GlobalConst.INCOMPLETE_DATA;
        }

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(filterModel.getItemBankId());
        if (itemBank == null) {
            return GlobalConst.ID_NOT_EXIST;
        }

        //格式化数据
        filterModel.setChapterIDs(StringUtil.formatSqlInString(filterModel.getChapterIDs()));
        filterModel.setQuestionCategoryIDs(StringUtil.formatSqlInString(filterModel.getQuestionCategoryIDs()));
        filterModel.setMandatoryIDs(StringUtil.formatSqlInString(filterModel.getMandatoryIDs()));

        //根据外部题型获取题型信息，根据获取的内部题型是否需要处理子试题
        QuestionType questionType = questionTypeService.getQuestionType(filterModel.getQuestionTypeID());
        if (questionType == null) {
            return GlobalConst.ID_NOT_EXIST;
        }

        //非子试题题型修订子题个数为0
        if (!(questionType.getQuestionInnerTypeID() == ItemType.READINGCOMPREHENSION.getIndex() ||
                questionType.getQuestionInnerTypeID() == ItemType.MATCH.getIndex() ||
                questionType.getQuestionInnerTypeID() == ItemType.CLOZE.getIndex() ||
                questionType.getQuestionInnerTypeID() == ItemType.COMPOSITE.getIndex() ||
                questionType.getQuestionInnerTypeID() == ItemType.TEXTENTRY.getIndex()
        )) {
            filterModel.setFieldcount(0);
        }

        return GlobalConst.SUCCESS;
    }
    //endregion

    //region 根据题库id，题型id，所属类别id查询试题id列表（策略组卷用）

    /**
     * 根据题库id，题型id，所属类别id查询试题id列表（策略组卷用）.
     *
     * @param itemBankId     题库id
     * @param typeCategories 题型，类别map
     * @return the questions by categories for rules
     */
    @Override
    public Map<String, List<String>> getQuestionsByCategoriesForRules(String itemBankId, Map<String, String> typeCategories) {

        Map<String, List<String>> resultMap = new LinkedHashMap<>();

        //循环所有筛选模型
        for (Map.Entry<String, String> map : typeCategories.entrySet()) {
            resultMap.put(map.getKey(), readMapper.getQuestionsByCategoriesForRules(itemBankId, map.getKey(), StringUtil.formatSqlInString(map.getValue())));
        }

        return resultMap;
    }
    //endregion

    //region 根据题库id获取指定题库下指定题型的试题按照给定的章节,应用类别，难度范围的数量统计列表(策略用)

    /**
     * 根据题库id获取指定题库下指定题型的试题按照给定的章节,应用类别统计的难中易三档题数列表.
     *
     * @param itemBankId 题库id
     * @param cntModel   统计参数模型
     * @return the question difficulty stat by item bank id
     */
    @Override
    public ReturnModel getQuestionDifficultyStatByItemBankId(String itemBankId, DifficultyCountModel cntModel) {
        ReturnModel model = new ReturnModel();
        List<Integer> list = new ArrayList<>();

        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        //check param
        String questionTypeId = cntModel.getQuestionType();
        if (questionTypeId.indexOf(',') >= 0) {
            model.setCode(GlobalConst.NOT_SUPPORT);
            model.setMessage("只支持单题型数据统计");
            return model;
        }

        if (cntModel.getChapter().contains("\'") || cntModel.getQuestionCategory().contains("\'")) {
            model.setCode(GlobalConst.DATA_FORMAT_ERROR);
            model.setMessage("章节或应用类别数据格式错误");
            return model;
        }

        //format param
        String chapterIds = StringUtil.formatSqlInString(cntModel.getChapter());
        String categoryIds = StringUtil.formatSqlInString(cntModel.getQuestionCategory());

        for (DifficultyRangeModel range : (new GlobalConst()).getBigDiffRange()) {
            int cnt = readMapper.getQuestionDifficultyStatByItemBankId(itemBankId, range.getMinValue(), range.getMaxValue(),
                    questionTypeId, chapterIds, categoryIds);
            list.add(cnt);
        }

        model.setCode(GlobalConst.SUCCESS);
        model.setData(list);
        return model;
    }
    //endregion

    //region 根据试题id列表获取策略检查用的试题信息列表（策略检查必选和例题用）

    /**
     * 根据试题id列表获取策略检查用的试题信息列表（策略检查必选和例题用）.
     *
     * @param itemIds the item ids
     * @return the question info for rule check
     */
    @Override
    public List<RuleCheckQuestionInfo> getQuestionInfoForRuleCheck(Map<String, Boolean> itemIds) {
        List<RuleCheckQuestionInfo> resultList = new ArrayList<>();

        for (Map.Entry<String, Boolean> itemId : itemIds.entrySet()) {
            ItemDto item = getItemInfo(itemId.getKey());
            if (item != null) {
                for (QuestionCategory category : item.getCategories()) {
                    RuleCheckQuestionInfo questionInfo = new RuleCheckQuestionInfo();
                    questionInfo.setQuestionId(item.getQuestion().getQuestionId());
                    questionInfo.setQuestionTypeId(item.getQuestion().getQuestionTypeId());
                    questionInfo.setDifficultyType(Function.getDifficultyType(item.getQuestion().getDifficulty()));
                    questionInfo.setQuestionCategoryId(category.getQuestionCategoryID());
                    questionInfo.setMust(itemId.getValue());
                    questionInfo.setSubItemCount(item.getQuestion().getFieldCount());
                    resultList.add(questionInfo);
                }
            }
        }

        return resultList;
    }
    //endregion

    //endregion

    //region 新增试题

    /**
     * 新增试题
     * v4版本 I8不支持汉字模式；I30只支持sx-xx
     *
     * @param questionModel 试题录入模型
     * @param source        来源：0，内部；1，外部
     * @param sourceType    来源类型：0，试题录入；1，试卷录入
     * @param inputType     录题方式：0，手工录入；1，文件导入
     * @param importType    导入方式：0，无；1，word导入；2，excel导入
     * @param appKey        app key
     * @return the return model
     */
    @Override
    public ReturnModel addQuestion(QuestionModel questionModel, int source, int sourceType, int inputType, int importType, String appKey) {

        String itemBankId = questionModel.getI4();

        ReturnModel model = checkItemBank(appKey, itemBankId);
        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        CourseItemBank itemBank = (CourseItemBank) model.getData();

        //得到试题应用类别(I8只能支持guid)
        List<String> categoryIds = checkCategoryList(questionModel.getI8());

            ReturnModel checkModel = checkQtiModel(questionModel, categoryIds);
        if (checkModel.getCode() != GlobalConst.SUCCESS) {
            return checkModel;
        }

        //生成试题id
        String questionId = UUID.randomUUID().toString();
        questionModel.setI1(questionId);

        //得到试题主体模型
        Question question = makeQuestion(questionModel, source, sourceType, inputType, importType);

        //得到qti的xml
        String xml = qtiService.getQuestionXml(questionModel);

        //得到章节id
        String chapterId = questionModel.getI5();

        //得到试题使用模型
        QuestionUseRelation useRelation = new QuestionUseRelation();
        useRelation.setQuestionId(questionId);
        useRelation.setAppId(itemBank.getAppID());
        useRelation.setCreateOrgId(itemBank.getCreateOrgID());
        useRelation.setItemBankId(itemBankId);
        useRelation.setStatus(SysState.START.getIndex());

        //得到试题章节模型
        QuestionChapterInfo chapterInfo = new QuestionChapterInfo();
        chapterInfo.setQuestionId(questionId);
        if (StringUtil.isEmpty(chapterId)) {
            chapterInfo.setChapterId(null);
        } else {
            chapterInfo.setChapterId(chapterId);
        }
        chapterInfo.setItemBankId(itemBankId);

        //保存入库
        saveAdd(question, xml, useRelation, chapterInfo, categoryIds);

        model.setCode(GlobalConst.SUCCESS);
        model.setData(questionId);
        return model;
    }

    //region 获取并检查应用类别列表，如果为null则表示存在非法数据

    /**
     * 获取并检查应用类别列表，如果为null则表示存在非法数据.
     *
     * @param list 原始列表
     * @return 检查后列表
     */
    private List<String> checkCategoryList(List<String> list) {
        List<String> result = new ArrayList<>();
        for (String id : list) {
            if (!StringUtil.isEmpty(id) && questionCategoryService.getQuestionCategoryInfo(id) != null) {
                result.add(id);
            }
        }

        if (result.size() != list.size()) {
            return new ArrayList<>();
        }

        return result;
    }
    //endregion

    //region 检查试题模型

    /**
     * 检查试题模型.
     *
     * @param model the model
     * @return the int
     */
    private int checkModel(QuestionModel model) {
        if (model.getI8().isEmpty()) {
            return GlobalConst.INCOMPLETE_DATA;
        }
        //难度
        if (model.getI13() < 0 || model.getI13() > 1) {
            return GlobalConst.DATA_FORMAT_ERROR;
        }
        //是否乱序
        if (model.getI11() != 0 && model.getI11() != 1) {
            return GlobalConst.DATA_FORMAT_ERROR;
        }
        //是否主观
        if (model.getI32() != 0  && model.getI32() != 1) {
            return GlobalConst.DATA_FORMAT_ERROR;
        }

        return GlobalConst.SUCCESS;
    }
    //endregion

    //region 检查模型数据

    /**
     * 检查模型数据
     *
     * @param questionModel 试题模型
     * @param categoryIds   the category ids
     * @return return model
     */
    public ReturnModel checkQtiModel(QuestionModel questionModel, List<String> categoryIds) {

        ReturnModel model = new ReturnModel();

        //qti模型检查
        int checkResult = qtiService.checkQuestionModel(questionModel);
        if (checkResult != GlobalConst.SUCCESS) {
            model.setCode(checkResult);
            model.setMessage(modelCheckFailed);
            return model;
        }

        //其余模型检查
        int checkResults = checkModel(questionModel);
        if (checkResults != GlobalConst.SUCCESS) {
            model.setCode(checkResults);
            model.setMessage(modelCheckFailed);
            return model;
        }

        //I30只能支持Sx-xx模式，不支持Guid
        QuestionType questionType = questionTypeService.getQuestionTypeBySectionId(questionModel.getI30());
        if (questionType == null) {
            model.setCode(GlobalConst.DATA_FORMAT_ERROR);
            model.setMessage("大题标识不存在");
            return model;
        }
        //修订i30
        questionModel.setI30(questionType.getQuestionTypeID());

        //得到试题应用类别(I8只能支持guid)
        if (categoryIds.isEmpty()) {
            model.setCode(GlobalConst.DATA_FORMAT_ERROR);
            model.setMessage("应用类别有误");
            return model;
        }

        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 根据试题得到试题主体

    /**
     * 根据试题得到试题主体.
     *
     * @param model      试题录入模型
     * @param source     来源：0，内部；1，外部
     * @param sourceType 来源类型：0，试题录入；1，试卷录入
     * @param inputType  录题方式：0，手工录入；1，文件导入
     * @param importType 导入方式：0，无；1，word导入；2，excel导入
     * @return the question
     */
    private Question makeQuestion(QuestionModel model, int source, int sourceType, int inputType, int importType) {
        Question question = new Question();

        question.setQuestionId(model.getI1());
        String title = qtiService.getTitle(model);
        question.setTitle(title == null ? "" : title);
        if (model.getI3() == ItemType.MATCH.getIndex() && StringUtil.isEmpty(question.getTitle())) {
            question.setTitle("匹配题");
        }

        question.setQuestionInnerTypeId(model.getI3());
        //内部未审核，外部审核通过，如果用户传递模型为审核通过也审核通过
        question.setIsCheck(CheckType.UNCHECK.getIndex());
        if (source == Source.OUTER.getIndex() || model.getI25() == CheckType.CHECKED.getIndex()) {
            question.setIsCheck(CheckType.CHECKED.getIndex());
        }

        // 如果提供章节则视为课程试题，否则视为试卷试题
        question.setIsCourseOnly(model.getI5().length() > 0 ? YesOrNo.YES.getIndex() : YesOrNo.NO.getIndex());
        //试题主体表该字段记录逗号分割的categoryid集合
        question.setQuestionCategory(String.join(",", model.getI8()));
        question.setScore(((int) model.getI9()) == 0 ? 1 : model.getI9());
        question.setQuestionAnalysis(model.getI10());
        question.setShuffle(model.getI11());
        question.setKeywords(model.getI12());
        question.setDifficulty(model.getI13());
        question.setFieldCount(model.getI17());
        if (model.getI19() != YesOrNo.NO.getIndex()) {
            question.setMediaType(model.getI19());
        } else {
            question.setMediaType(0);
        }
        if (!StringUtil.isEmpty(model.getI20())) {
            question.setMediaDuration(Integer.parseInt(model.getI20()));
        } else {
            question.setMediaDuration(0);
        }
        question.setCreater(model.getI22());
        question.setCreateTime(DateUtil.toShowString(new Date()));
        question.setLastUpdateTime(null);
        int masterLevel = (model.getI26() <= 0 || model.getI26() > 3) ? 1 : model.getI26();
        question.setMasterLevel(masterLevel);
        int perceiver = (model.getI27() <= 0 || model.getI27() > 3) ? 1 : model.getI27();
        question.setPerceiver(perceiver);
        question.setResponseTime(model.getI31());
        //只有填空题才可以区分主客观
        if (model.getI3() == ItemType.TEXTENTRY.getIndex()) {
            question.setIsSubjective(model.getI32());
        } else {
            //试题主客观标记：客观题0，主观题1
            question.setIsSubjective(0);
        }
        //I30被转换了，从sx-xx到guid
        question.setQuestionTypeId(model.getI30());
        question.setDifference(1);
        question.setSequenceIndex(0);
        // 默认不是子试题
        question.setIsSubQuestion(YesOrNo.NO.getIndex());
        // 默认启用
        question.setStatus(SysState.START.getIndex());
        // 试题来源
        question.setSource(source);
        question.setSourceType(sourceType);
        question.setInputType(inputType);
        question.setImportType(importType);
        //新增的试题一律私有
        question.setUseType(UseType.PRIVATE.getIndex());
        // 父试题标识
        question.setParentQuestionId(question.getQuestionId());
        // 是否有子试题
        question.setIsSubQuestion(YesOrNo.NO.getIndex());
        // 区分度
        question.setDifference(1);
        // 状态
        question.setStatus(SysState.START.getIndex());
        question.setComment("");
        question.setDisplayCount(0);
        question.setQuestionVersion(0);
        question.setIsPublish(0);
        question.setQualityGrade(0);

        return question;
    }
    //endregion

    //region 根据试题id更新对应试题的应用类别关系

    /**
     * 根据试题id更新对应试题的应用类别关系.
     *
     * @param id          试题id
     * @param categoryIds 应用类别id列表
     * @return the boolean
     */
    private boolean updateQuestionCategoryRelation(String id, List<String> categoryIds) {
        //如果试题id不存在，则返回错误
        if (null == getItem(id)) {
            return false;
        }

        //如果类别数量小于1，则返回错误
        if (categoryIds.isEmpty()) {
            return false;
        }

        //根据试题id删除所有类别关系
        writeMapper.deleteQuestionCategoryRelation(id);
        //循环所有类别id，逐个添加关系
        for (String categoryId : categoryIds) {
            QuestionCategoryRelation relation = new QuestionCategoryRelation();
            RegxUtil regxUtil = new RegxUtil();
            if (!regxUtil.regx(categoryId)) {
                categoryId = readMapper.getByQuestionCategoryId(categoryId);
            }
            relation.setQuestionCategoryId(categoryId);
            relation.setQuestionId(id);
            writeMapper.insertQuestionCategoryRelation(relation);
        }

        return true;
    }
    //endregion

    //region 新增试题入库操作

    /**
     * 新增试题入库操作.
     *
     * @param question    试题实体模型
     * @param xml         试题xml
     * @param useRelation 试题题库使用模型
     * @param chapterInfo 试题章节模型
     * @param categoryIds 试题应用类别列表
     * @return the int
     */
    private int saveAdd(Question question, String xml, QuestionUseRelation useRelation, QuestionChapterInfo chapterInfo, List<String> categoryIds) {

        String questionId = question.getQuestionId();
        if(question.getResponseTime()==null){
            question.setResponseTime(0);
        }
        writeMapper.insert(question);

        QuestionContent content = new QuestionContent();
        content.setQuestionId(questionId);
        content.setContentXml(xml);
        content.setContentJson("");
        writeMapper.insertQuestionContent(content);

        updateQuestionCategoryRelation(questionId, categoryIds);

        writeMapper.insertQuestionUseRelation(useRelation);

        writeMapper.insertQuestionChapterInfo(chapterInfo);

        //添加试题后获得试题json 信息后更新内容表
        updateJson(useRelation.getItemBankId(), questionId);

        //更新包含阿里云图片的QuestionUseImage表数据

        return GlobalConst.SUCCESS;
    }
    //endregion

    /**
     * 更新试题表json.
     *
     * @param itemBankId the item bank id
     * @param questionId the id
     */
    private void updateJson(String itemBankId, String questionId) {
        String json = JsonUtil.getJson(getItemBankQuestion(itemBankId, questionId, AnswerType.ALL).getData())
                .replace("\"", "\\\"").replace("'", "''");

        writeMapper.updateQuestionContentJson(questionId, json);
    }

    //endregion

    // region 删除试题

    //region 临时删除试题

    /**
     * 临时删除试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return return model
     */
    @Override
    public ReturnModel deleteQuestion(String itemBankId, String id, String appKey) {
        ReturnModel model = basicCheck(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        Question question = (Question) model.getData();

        if (question.getStatus() != SysState.START.getIndex() && question.getStatus() != SysState.STOP.getIndex()) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage("无法删除试题");
            return model;
        }

        //将使用关系表状态置为-1
        writeMapper.updateQuestionStatus(id, itemBankId, SysState.DELETE.getIndex());
        model.setCode(GlobalConst.SUCCESS);
        model.setData(null);
        return model;
    }
    //endregion

    //region  批量临时删除试题

    /**
     * 批量临时删除试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param idList     试题id集合
     * @return return model
     */
    @Override
    public ReturnModel deleteQuestionList(String itemBankId, List<String> idList, String appKey) {
        ReturnModel model = new ReturnModel();

        DeleteListResult listResult = new DeleteListResult();

        for (int i = 0; i < idList.size(); i++) {
            model = deleteQuestion(itemBankId, idList.get(i), appKey);
            listResult.getResult(model);
        }

        model.setMessage(listResult.getMessage("删除"));

        return model;
    }
    //endregion

    //region  恢复试题

    /**
     * 恢复试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return return model
     */
    @Override
    public ReturnModel recover(String itemBankId, String id, String appKey) {
        ReturnModel model = basicCheck(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        Question question = (Question) model.getData();

        if (question.getStatus() != SysState.DELETE.getIndex()) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage("无法恢复试题");
            return model;
        }

        //将使用关系表状态置为0
        writeMapper.updateQuestionStatus(id, itemBankId, SysState.START.getIndex());

        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region  批量恢复试题

    /**
     * 批量恢复试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param idList     试题id集合
     * @return return model
     */
    @Override
    public ReturnModel recoverList(String itemBankId, List<String> idList, String appKey) {
        ReturnModel model = new ReturnModel();

        DeleteListResult listResult = new DeleteListResult();

        for (int i = 0; i < idList.size(); i++) {
            ReturnModel result = recover(appKey, itemBankId, idList.get(i));
            listResult.getResult(result);
        }

        model.setCode(GlobalConst.SUCCESS);
        model.setMessage(listResult.getMessage("恢复"));

        return model;
    }
    //endregion

    //region  彻底删除试题

    /**
     * 彻底删除试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the return model
     */
    @Override
    public ReturnModel removeQuestion(String itemBankId, String id, String appKey) {
        ReturnModel model = basicCheck(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        Question question = (Question) model.getData();

        if (question.getStatus() != SysState.DELETE.getIndex()) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage("无法彻底删除试题");
            return model;
        }

        //彻底删除前需要确认试题不存在关联等信息方可删除

        // 物理删除 - 试题内容
        writeMapper.deleteQuestionContent(id);
        // 物理删除 - 试题分类关系表
        writeMapper.deleteQuestionCategoryRelation(id);
        // 物理删除 - 试题章节表
        writeMapper.deleteQuestionChapterInfo(id);
        // 物理删除 - 试卷试题曝光表
        writeMapper.deleteQuestionDisplayInfo(id);
        // 物理删除 - 试题扩展值表
        writeMapper.deleteQuestionExendFieldValue(id);
        // 物理删除 - 策略试题曝光表
        writeMapper.deleteQuestionRule(id);
        // 物理删除 - 试题使用关系表
        writeMapper.deleteQuestionUseRelation(id);
        // 物理删除 - 试题互斥表
        writeMapper.deleteRepulsiveQuqestionID(id);
        // 物理删除 - 试题信息
        writeMapper.deleteQuestion(id);

        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region  批量彻底删除试题

    /**
     * 批量彻底删除试题
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param idList     试题id集合
     * @return return model
     */
    @Override
    public ReturnModel removeQuestionList(String itemBankId, List<String> idList, String appKey) {
        ReturnModel model = new ReturnModel();

        DeleteListResult listResult = new DeleteListResult();

        for (int i = 0; i < idList.size(); i++) {
            ReturnModel result = removeQuestion(itemBankId, idList.get(i), appKey);
            listResult.getResult(result);
        }

        model.setCode(GlobalConst.SUCCESS);
        model.setMessage(listResult.getMessage("彻底删除"));

        return model;
    }
    //endregion

    //endregion

    //region 审核试题

    /**
     * 审核试题(只能修改私有状态的试题).
     *
     * @param appKey      appkey
     * @param itemBankId  题库id
     * @param id          试题id
     * @param checkStatus 目标审核状态：true 审核通过；false  审核拒绝
     * @return the return model
     */
    @Override
    public ReturnModel updateCheckStatus(String itemBankId, String id, boolean checkStatus, String appKey) {
        ReturnModel model = getPrivateQuestion(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        Question question = (Question) model.getData();

        // 只有启用的试题可以审核
        if (question.getStatus() != SysState.START.getIndex()) {
            model.setCode(GlobalConst.NOT_SUPPORT);
            model.setMessage("试题未启用，无法修改审核状态");
            return model;
        }

        int newCheckStatus = checkStatus ? CheckType.CHECKED.getIndex() : CheckType.NOCHECKED.getIndex();
        if (question.getIsCheck() == CheckType.CHECKED.getIndex() && !hasRelation(id)) {
            //有关联的不可以取消审核
            newCheckStatus = CheckType.UNCHECK.getIndex();
        }

        writeMapper.updateQuestionCheckStatus(id, newCheckStatus, DateUtil.getCurrentTimeToShowString());
        writeMapper.updateLastUpdateTime(id, DateUtil.toShowString(new Date()));

        model.setCode(GlobalConst.SUCCESS);
        model.setData(null);
        return model;
    }
    //endregion

    //region 停启用试题

    /**
     * 停启用试题.
     * 已经修订为修改试题使用关系表的停启用标记，试题表的statu暂停使用
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the return model
     */
    @Override
    public ReturnModel updateStatus(String itemBankId, String id, String appKey) {
        ReturnModel model = basicCheck(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        QuestionUseRelation questionUseRelation = readMapper.getQuestionUseRelation(itemBankId, id);

        if (questionUseRelation.getStatus() == SysState.CLOSE.getIndex() || questionUseRelation.getStatus() == SysState.DELETE.getIndex()) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage(noPermission);
            return model;
        }

        int newStatus = SysState.STOP.getIndex();
        //有关联的试题停用后不可以启用
        if (questionUseRelation.getStatus() == SysState.STOP.getIndex()) {
            newStatus = SysState.START.getIndex();
        }

        writeMapper.updateQuestionStatus(id, itemBankId, newStatus);
        writeMapper.updateLastUpdateTime(id, DateUtil.toShowString(new Date()));

        model.setCode(GlobalConst.SUCCESS);
        model.setData(null);
        return model;
    }
    //endregion

    //region 试题修改

    //region 修改试题

    /**
     * 修改试题.
     *
     * @param appKey        appkey
     * @param questionId    试题id
     * @param questionModel 试题修改模型
     * @return the return model
     */
    @Override
    public ReturnModel updateQuestion(String itemBankId, String questionId, QuestionModel questionModel, String appKey) {

        questionModel.setI1(questionId);

        //效验权限
        ReturnModel model = checkItemBank(appKey, itemBankId);
        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        //查询旧试题信息
        Question question = getItem(questionId);
        if (question == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage("试题id不存在");
            return model;
        }

        //得到qti的xml
        String xml = qtiService.getQuestionXml(questionModel);

        //如该课程是共享状态，需要创建新试题，同时返回新试题标识
        if (question.getUseType() == UseType.SHARE.getIndex()) {
            return updateNewQuestion(question, questionModel, appKey, itemBankId, questionId);
        }

        //如该课程不是共享状态，则更新试题内容
        //只和一个试卷有关系
        if (question.getDisplayCount() <= 1) {
            //得到试题应用类别(I8只能支持guid)
            List<String> categoryIds = checkCategoryList(questionModel.getI8());

            //检查模型数据
            ReturnModel checkModel = checkQtiModel(questionModel, categoryIds);
            if (checkModel.getCode() != GlobalConst.SUCCESS) {
                return checkModel;
            }

            //得到试题章节模型
            QuestionChapterInfo chapterInfo = new QuestionChapterInfo();
            chapterInfo.setQuestionId(questionId);
            chapterInfo.setChapterId(questionModel.getI5());
            chapterInfo.setItemBankId(itemBankId);

            saveEdit(makeUpdateQuestion(questionModel), xml, chapterInfo, categoryIds);

            model.setCode(GlobalConst.SUCCESS);
            model.setData(questionId);
            return model;
        }

        //和多个试卷有关系
        ReturnModel updateNewQuestionModel = updateNewQuestion(question, questionModel, appKey, itemBankId, questionId);
        if (updateNewQuestionModel.getCode() != GlobalConst.SUCCESS) {
            return updateNewQuestionModel;
        }

        String newQuestionId = (String) updateNewQuestionModel.getData();
        model.setCode(GlobalConst.SUCCESS);
        model.setData(newQuestionId);
        return model;
    }
    //endregion

    //region 根据试题得到修改试题主体

    /**
     * 根据试题得到修改试题主体.
     *
     * @param model 试题录入模型
     * @return the question
     */
    private Question makeUpdateQuestion(QuestionModel model) {
        Question question = new Question();

        String title = qtiService.getTitle(model);
        question.setQuestionId(model.getI1());
        question.setTitle(title == null ? "" : title);
        if (model.getI3() == ItemType.MATCH.getIndex() && StringUtil.isEmpty(question.getTitle())) {
            question.setTitle("匹配题");
        }
        //试题主体表该字段记录逗号分割的categoryid集合
        question.setQuestionCategory(String.join(",", model.getI8()));
        question.setScore(((int) model.getI9()) == 0 ? 1 : model.getI9());
        question.setQuestionAnalysis(model.getI10());
        question.setShuffle(model.getI11());
        question.setKeywords(model.getI12());
        question.setDifficulty(model.getI13());
        question.setFieldCount(model.getI17());
        question.setModifier(model.getI22());
        question.setModifyTime(DateUtil.getCurrentTimeToShowString());
        question.setLastUpdateTime(DateUtil.getCurrentTimeToShowString());
        int masterLevel = (model.getI26() <= 0 || model.getI26() > 3) ? 1 : model.getI26();
        question.setMasterLevel(masterLevel);
        int perceiver = (model.getI27() <= 0 || model.getI27() > 3) ? 1 : model.getI27();
        question.setPerceiver(perceiver);
        question.setResponseTime(model.getI31());
        question.setIsSubjective(model.getI32());

        return question;
    }
    //endregion

    //region 修改试题入库操作

    /**
     * 修改试题入库操作.
     *
     * @param question    试题实体模型
     * @param xml         试题xml
     * @param chapterInfo 试题章节模型
     * @param categoryIds 试题应用类别列表
     * @return the int
     */
    private int saveEdit(Question question, String xml, QuestionChapterInfo chapterInfo, List<String> categoryIds) {

        String questionId = question.getQuestionId();
        if (StringUtil.isEmpty(question.getLastUpdateTime())) {
            question.setLastUpdateTime(DateUtil.toShowString(new Date()));
        }
        //更新试题主表
        writeMapper.update(question);
        //更新试题内容表
        writeMapper.updateQuestionContent(questionId, xml);
        //更新试题应用类别
        updateQuestionCategoryRelation(questionId, categoryIds);
        chapterInfo.setChapterId(null);
        //更新试题章节
        writeMapper.updateQuestionChapterInfo(questionId, chapterInfo.getItemBankId(), chapterInfo.getChapterId());

        //添加试题后获得试题json 信息后更新内容表
        updateJson(chapterInfo.getItemBankId(), questionId);

        //更新包含阿里云图片的QuestionUseImage表数据

        return GlobalConst.SUCCESS;
    }
    //endregion

    //region 更新一道新试题

    /**
     * 更新一道新试题.
     *
     * @param question      the question
     * @param questionModel 试题修改模型
     * @param appKey        the app key
     * @param itemBankId    the item bank id
     * @param questionId    the question id
     * @return the return model
     */
    private ReturnModel updateNewQuestion(Question question, QuestionModel questionModel, String appKey, String itemBankId, String questionId) {


        ReturnModel model = addQuestion(questionModel, question.getSource(),
                question.getSourceType(), question.getInputType(), question.getImportType(),
                appKey);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        //得到新增试题id
        String newQuestionId = (String) model.getData();
        //将原试题关闭
        writeMapper.updateQuestionStatus(questionId, itemBankId, SysState.CLOSE.getIndex());

        model.setCode(GlobalConst.SUCCESS);
        model.setData(newQuestionId);
        return model;
    }
    //endregion

    //endregion

    //region 试题状态批量修改

    /**
     * 试题状态批量修改
     *
     * @param itemBankId 题库id
     * @param appKey     appkey
     * @return
     */
    @Override
    public ReturnModel batchUpdateStatus(String itemBankId, UpdateQuestionStatus statusModel, String appKey) {
        ReturnModel model = new ReturnModel();

        if (statusModel.getStatus() != SysState.START.getIndex() && statusModel.getStatus() != SysState.STOP.getIndex()) {
            model.setCode(GlobalConst.DATA_FORMAT_ERROR);
            model.setMessage("数据格式错误");
            return model;
        }
        //确认除非非限定appid否则只能修改appid对应的itemBankId下的试题
        if (!itemBankService.hasPermission(itemBankId, appKey)) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage(noPermission);
            return model;
        }
        String json = String.join("','", statusModel.getIdList()).replace("[", "").replace("]", "");
        //
        int result = writeMapper.batchUpdateStatus(itemBankId, statusModel.getStatus(), json, SysState.START.getIndex(), SysState.STOP.getIndex());
        if (result <= 0) {
            model.setCode(GlobalConst.DATA_BASE_ERROR);
            model.setMessage("数据库操作失败");
            model.setData(result);
            return model;
        }
        model.setCode(GlobalConst.SUCCESS);
        model.setData(result);
        return model;
    }

    //endregion

    //region 试题列表

    /**
     * 试题列表
     *
     * @param filterModelDto 试题筛选模型
     * @param isOuter        是否外部调用
     * @param isGetContent   是否获取试题内容
     * @return return model
     */
    @Override
    public ReturnModel getList(FilterModelDto filterModelDto, Boolean isOuter, Boolean isGetContent) {
        ReturnModel model = new ReturnModel();
        filterModelDto.setItembankId(filterModelDto.getItembankId());

        //合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(filterModelDto.getItembankId());
        if (itemBank == null) {
            model.setCode(GlobalConst.COURSE_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }
        //获取查询结果
        Map<String, Object> map = getBasicSearchResult(filterModelDto, isOuter);
        //获取所有试题列表
        List<QueryFilterModelDto> questionList = (List<QueryFilterModelDto>) map.get(resultsString);
        //获取总个数
        int count = (int) map.get(countsString);

        JSONObject json = new JSONObject();
        json.put(countsString, count);
        json.put(rowsString, getListForQuestionModel(questionList, isGetContent));

        model.setData(json);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }

    //endregion

    //region 获取试题回收站列表

    /**
     * 获取试题回收站列表
     *
     * @param filterModelDto 筛选模型
     * @return return model
     */
    @Override
    public ReturnModel getRycleBinList(FilterModelDto filterModelDto) {

        String type = "recycle";
        ReturnModel model = new ReturnModel();

        //region 合法性
        //题库合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(filterModelDto.getItembankId());
        if (itemBank == null) {
            model.setCode(GlobalConst.COURSE_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        //endregion

        //获取查询用sql
        String recyleBinListSql = CommonFunction.recyleBinListSql(filterModelDto);
        //获取所有试题回收站列表
        List<QueryFilterModelDto> recyleBinList = readMapper.getListQuery(type, filterModelDto, recyleBinListSql);

        //获取总个数
        int count = readMapper.getListCount(type, recyleBinListSql);

        List<QuestionModel> list = new ArrayList<>();

        //循环取值赋值
        for (QueryFilterModelDto row : recyleBinList) {
            // 获取qti
            QuestionModel questionModel = new QuestionModel();

            QuestionType questionType = questionTypeService.getQuestionType(row.getQuestionTypeId());

            list.add(ModelTransfer.getQuestionModeFromRow(row, getQuestionCategoryNames(row.getQuestionId()), questionType, questionModel));
        }

        //转json对象
        JSONObject json = new JSONObject();
        json.put(rowsString, list);
        json.put(countsString, count);

        model.setData(json);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }

    //endregion

    //region 共享试题

    //region 共享试题

    /**
     * 共享试题.
     *
     * @param sourceItemBankId 源题库id
     * @param targetItemBank   目标题库模型
     * @param chapterRelation  旧新章节对照map
     * @return the return model
     */
    @Override
    public ReturnModel shareQuestion(String sourceItemBankId, CourseItemBank targetItemBank, Map<String, String> chapterRelation) {
        ReturnModel model = new ReturnModel();

        //将源题库ID下的所有试题在【课程题库信息表】的使用类型标记为共享状态；
        writeMapper.shareQuestion(sourceItemBankId);

        //并且将在【试题题库使用关系表】下源题库ID下的这些试题题库关系复制到目标题库ID下；
        writeMapper.copyQuestionRelation(sourceItemBankId, targetItemBank.getItemBankID(), targetItemBank.getAppID(),
                targetItemBank.getCreateOrgID());

        //读取【试题章节信息表】中源题库ID下所有试题章节关系；
        //根据新旧章节对照关系字段，复制生成目标题库的章节试题关系；
        int chapterResult = saveChapterRelation(sourceItemBankId, targetItemBank.getItemBankID(), chapterRelation);

        if (chapterResult < 0) {
            //删除试题题库复制关系
            writeMapper.deleteQuestionUseRelationByItemBank(targetItemBank.getItemBankID());

            model.setCode(chapterResult);
            model.setMessage("章节关系复制失败");
            return model;
        }

        model.setCode(GlobalConst.SUCCESS);
        model.setData(null);
        return model;
    }

    //endregion

    //region 保存章节对照关系

    /**
     * 保存章节对照关系.
     *
     * @param sourceItemBankId 源题库id
     * @param targetItembankId 目标题库id
     * @param chapterRelation  旧新章节对照map
     * @return the int
     */
    private int saveChapterRelation(String sourceItemBankId, String targetItembankId, Map<String, String> chapterRelation) {
        //查询源题库课程下所有章节试题关系
        List<QuestionChapterInfo> sourceChapters = readMapper.getQuestionChapterByItemBank(sourceItemBankId);

        if (sourceChapters.isEmpty()) {
            return 999;
        }

        //将数据转换成为目标数据
        List<QuestionChapterInfo> targetChapters = new ArrayList<>();

        for (QuestionChapterInfo qc : sourceChapters) {
            boolean isExist = false;
            for (Map.Entry<String, String> relation : chapterRelation.entrySet()) {
                if (relation.getKey().equalsIgnoreCase(qc.getChapterId())) {
                    QuestionChapterInfo info = new QuestionChapterInfo();
                    info.setChapterId(relation.getValue());
                    info.setItemBankId(targetItembankId);
                    info.setQuestionId(qc.getQuestionId());

                    targetChapters.add(info);
                    isExist = true;
                }
            }

            if (!isExist) {
                QuestionChapterInfo info = new QuestionChapterInfo();
                info.setChapterId(GlobalConst.EMPTYUUID);
                info.setItemBankId(qc.getItemBankId());
                info.setQuestionId(qc.getQuestionId());

                targetChapters.add(info);
            }
        }

        //插入目标数据
        for (QuestionChapterInfo qc : targetChapters) {
            writeMapper.insertQuestionChapterInfo(qc);
        }

        return GlobalConst.SUCCESS;
    }
    //endregion

    //endregion

    //region 试题导出

    /**
     * 试题导出.
     *
     * @param filterModelDto the filter model dto
     * @return the return model
     */
    @Override
    public ReturnModel exportQuestion(FilterModelDto filterModelDto) throws PoiException, ZipException, IOException {
        //获取试题集合
        ReturnModel model = new ReturnModel();

        //合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(filterModelDto.getItembankId());
        if (itemBank == null) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        //获取查询结果
        Map<String, Object> map = getBasicSearchResult(filterModelDto, false);
        //获取所有试题列表
        List<QueryFilterModelDto> questionList = (List<QueryFilterModelDto>) map.get(resultsString);

        //转化为word通用泛型
        List<QuestionImportModel> wordModelList = getQuestionImportModelFromFilterModel(questionList);

        String zipFileName = makeZipFile(wordModelList);

        model.setData(zipFileName);
        model.setCode(GlobalConst.SUCCESS);

        return model;
    }

    //endregion

    //region 私有方法

    //region 根据试题id获取试题信息

    /**
     * 根据试题id获取试题信息.
     *
     * @param id 试题id
     * @return 试题基本信息
     */
    private ItemDto getItemInfo(String id) {
        ItemDto item = new ItemDto();

        //检查id合法性
        if (!UUIDUtil.isUUID(id)) {
            return null;
        }

        //获取试题模型
        Question question = getItem(id);
        //获取试题qti
        QuestionContent content = readMapper.getContentById(id);

        String xml = "";
        if (content != null) {
            xml = content.getContentXml();
        }

        //两次查询均失败，则视为试题不存在
        if (question == null || xml.length() == 0) {
            return null;
        }

        item.setQuestion(question);
        item.setContentXml(xml);

        //获取试题内部题型id
        item.setQuestionTypeId(question.getQuestionInnerTypeId());

        //获得试题题型的大题编号sectionId
        QuestionType questionType = questionTypeService.getQuestionType(question.getQuestionTypeId());
        if (questionType == null) {
            return null;
        }

        String sectionId = questionType.getSectionID();
        item.setSectionId(sectionId);

        //得到试题类别列表
        item.setCategories(readMapper.getQuestionCategorys(id));

        return item;
    }
    //endregion

    //region 生成试题模型

    /**
     * 生成试题模型.
     *
     * @param item        试题传输模型
     * @param itemBank    题库模型
     * @param chapterInfo 章节模型
     * @param needAnswer  是否需要答案
     * @return 试题模型
     */
    private QuestionModel getQuestionModel(ItemDto item, CourseItemBank itemBank, QuestionChapterInfo chapterInfo, boolean needAnswer) {

        if (item == null || item.getQuestionTypeId() == 0 || item.getContentXml().length() == 0 || itemBank == null) {
            return null;
        }

        QuestionModel model = qtiService.getQuestion(item.getQuestionTypeId(), needAnswer, item.getContentXml());

        //根据非QTI属性赋值
        model.setI3(item.getQuestionTypeId());
        model.setI4(itemBank.getItemBankID());
        model.setI5(chapterInfo == null ? "" : chapterInfo.getChapterId());

        //试题类别
        model.setI8(getQuestionCategoryName(item.getCategories()));
        model.setI9(item.getQuestion().getScore());
        // 如果question 表里没有就从QTI 取  为了兼容老数据
        if (needAnswer) {
            model.setI10(item.getQuestion().getQuestionAnalysis().length() != 0 ? item.getQuestion().getQuestionAnalysis() : model.getI10());
        }
        //默认不乱序
        model.setI11(0);
        model.setI12(item.getQuestion().getKeywords());
        model.setI13(item.getQuestion().getDifficulty());
        model.setI14(itemBank.getCreateOrgID());

        //默认认为没有子试题
        model.setI17(model.getSub() == null ? 0 : model.getSub().size());
        model.setI18(itemBank.getAppID());
        model.setI19(item.getQuestion().getMediaType());
        model.setI20(String.valueOf(item.getQuestion().getMediaDuration()));
        model.setI22(String.format("%s,%s", item.getQuestion().getCreater(), item.getQuestion().getModifier()));
        model.setI23(item.getQuestion().getCreateTime());
        model.setI24(item.getQuestion().getLastUpdateTime());
        model.setI25(item.getQuestion().getIsCheck());
        model.setI26(item.getQuestion().getMasterLevel());
        model.setI27(item.getQuestion().getPerceiver());
        model.setI28(item.getQuestion().getSerialNumber());
        model.setI29(item.getQuestion().getDisplayTime());
        model.setI30(item.getSectionId());
        model.setI31(item.getQuestion().getResponseTime());
        model.setI32(item.getQuestion().getIsSubjective());

        return model;
    }
    //endregion

    //region 检查题库权限

    /**
     * 检查题库权限.
     *
     * @param appKey     the app key
     * @param itemBankId the item bank id
     * @return the return model
     */
    private ReturnModel checkItemBank(String appKey, String itemBankId) {
        ReturnModel model = new ReturnModel();

        //关键数据缺失
        if (StringUtil.isEmpty(itemBankId)) {
            model.setCode(GlobalConst.INCOMPLETE_DATA);
            model.setMessage("参数不完整，缺少题库ID");
            return model;
        }

        //合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.COURSE_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }

        //确认除非非限定appid否则只能修改appid对应的itemBankId下的试题
        if (!itemBankService.hasPermission(itemBankId, appKey)) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage(noPermission);
            return model;
        }

        model.setData(itemBank);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 根据appkey，题库id和试题id检测试题状态，所有权

    /**
     * 根据appkey，题库id和试题id检测试题状态，所有权.
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the return model
     */
    private ReturnModel basicCheck(String appKey, String itemBankId, String id) {
        ReturnModel model = checkQuestion(itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        //确认除非非限定appid否则只能修改appid对应的itemBankId下的试题
        if (!itemBankService.hasPermission(itemBankId, appKey)) {
            model.setCode(GlobalConst.NO_PERMISSION);
            model.setMessage(noPermission);
            return model;
        }

        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 获取私有状态下的指定试题信息

    /**
     * 获取私有状态下的指定试题信息.
     *
     * @param appKey     appkey
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the return model
     */
    private ReturnModel getPrivateQuestion(String appKey, String itemBankId, String id) {
        ReturnModel model = basicCheck(appKey, itemBankId, id);

        if (model.getCode() != GlobalConst.SUCCESS) {
            return model;
        }

        //获取私有试题
        Question question = (Question) model.getData();

        if (question.getUseType() != UseType.PRIVATE.getIndex()) {
            model.setCode(GlobalConst.NO_PRIVATE);
            model.setMessage("试题处于共享状态，无法操作");
            return model;
        }

        model.setData(question);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 根据appkey，题库id和试题id检测题库，试题，以及题库试题关系

    /**
     * 根据appkey，题库id和试题id检测题库，试题，以及题库试题关系
     *
     * @param itemBankId 题库id
     * @param id         试题id
     * @return the return model
     */
    public ReturnModel checkQuestion(String itemBankId, String id) {
        ReturnModel model = new ReturnModel();

        //id合法性
        if (StringUtil.isEmpty(itemBankId) || StringUtil.isEmpty(id)) {
            model.setCode(GlobalConst.INCOMPLETE_DATA);
            model.setMessage("参数不完整");
            return model;
        }

        //题库合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.COURSE_NOT_EXIST);
            model.setMessage("题库id不存在");
            return model;
        }

        //试题合法性
        Question question = getItem(id);
        //试题表上的状态标记如果为删除则视为试题不存在，可以用这个标记为处理物理删除试题，与课程无关
        if (question == null || question.getStatus() == SysState.DELETE.getIndex()) {
            model.setCode(GlobalConst.ID_NOT_EXIST);
            model.setMessage("试题id不存在");
            return model;
        }
        model.setData(question);

        //题库和试题匹配
        QuestionUseRelation relation = readMapper.getQuestionUseRelation(itemBankId, id);
        if (relation == null) {
            model.setCode(GlobalConst.QUESTION_ITEM_BANK_NOT_MATCH);
            model.setData(null);
            model.setMessage("指定题库下无指定试题信息");
            return model;
        }

        //将题库试题关系上的试题状态装载到试题上，默认不使用试题上的状态
        question.setStatus(relation.getStatus());

        model.setCode(GlobalConst.SUCCESS);
        return model;
    }
    //endregion

    //region 判断试题id在曝光表和策略试题关系表中是否存在使用关系

    /**
     * 判断试题id在曝光表和策略试题关系表中是否存在使用关系.
     *
     * @param id 试题id
     * @return true 存在，false 不存在
     */
    private boolean hasRelation(String id) {
        return readMapper.getQuestionRelationCount(id, "") != 0;
    }
    //endregion

    //region 试题基础查询，返回result和count，分别表示查询结果List<QueryFilterModelDto>和条数count

    /**
     * 试题基础查询，返回result和count，分别表示查询结果List<QueryFilterModelDto>和条数count.
     *
     * @param filterModelDto 筛选模型
     * @param isOuter        是否是外部接口调用(外部调用时候sql查询条件不一样)
     * @return the baic search result
     */
    private Map<String, Object> getBasicSearchResult(FilterModelDto filterModelDto, boolean isOuter) {
        //获取查询用sql
        String questionListSql = CommonFunction.questionListSql(filterModelDto, isOuter);
        //获取查询的试题列表
        List<QueryFilterModelDto> questionList = readMapper.getListQuery("", filterModelDto, questionListSql);
        //获取查询的总条数
        int count = readMapper.getListCount("", questionListSql);

        Map<String, Object> map = new HashedMap();
        map.put(resultsString, questionList);
        map.put(countsString, count);

        return map;
    }
    //endregion

    //region 根据试题查询结果模型转换成试题模型

    /**
     * 根据试题查询结果模型转换成试题模型.
     *
     * @param questionList 试题查询结果模型
     * @param isGetContent 是否获取试题内容数据
     * @return the list for question model
     */
    private List<QuestionModel> getListForQuestionModel(List<QueryFilterModelDto> questionList, boolean isGetContent) {
        List<QuestionModel> list = new ArrayList<>();
        String questionTypeId = "";
        QuestionType questionType = null;

        //循环取值赋值
        for (QueryFilterModelDto row : questionList) {
            // 获取qti接口
            QuestionModel questionModel = new QuestionModel();
            //是否获取内容
            if (isGetContent) {
                questionModel = qtiService.getQuestion(row.getQuestionInnerTypeId(), true, row.getContentXml());
            }
            //为了减少开销，用变量questionTypeId保存了一下之前的结果，判断当前记录是否发生变更，如果变更则重新获取，否则使用历史，以减少rpc请求
            if (!questionTypeId.equalsIgnoreCase(row.getQuestionTypeId())) {
                questionTypeId = row.getQuestionTypeId();
                questionType = questionTypeService.getQuestionType(questionTypeId);
            }

            list.add(ModelTransfer.getQuestionModeFromRow(row, getQuestionCategoryNames(row.getQuestionId()), questionType, questionModel));
        }

        return list;
    }
    //endregion

    //region 根据试题获得试题类别列表

    /**
     * 根据试题获得试题类别列表
     *
     * @param questionId 试题id
     * @return question category list
     */
    private List<String> getQuestionCategoryNames(String questionId) {

        List<String> list = new ArrayList<>();

        List<QuestionCategory> dt = readMapper.getQuestionCategorys(questionId);

        for (QuestionCategory i : dt) {
            list.add(i.getQuestionCategoryName());
        }

        return list;
    }

    //endregion

    //region 得到试题应用类别名称列表

    /**
     * 得到试题应用类别名称列表.
     *
     * @param categories 应用类别列表
     * @return 试题应用类别名称列表.
     */
    private List<String> getQuestionCategoryName(List<QuestionCategory> categories) {
        List<String> list = new ArrayList<>();
        for (QuestionCategory category : categories) {
            list.add(category.getQuestionCategoryName());
        }

        return list;
    }
    //endregion

    //region组织word所需数据

    /**
     * 组织word所需数据
     *
     * @param list the list
     * @return return model
     * @throws IOException the io exception
     */
    private List<QuestionImportModel> getQuestionImportModelFromFilterModel(List<QueryFilterModelDto> list) {

        List<QuestionImportModel> models = new ArrayList<>();
        String titleString = "[题干] ";
        String optionString = "[选项] ";
        String answerString = "[答案] ";

        for (QueryFilterModelDto dto : list) {
            //组织文件内容
            QuestionImportModel questionImportModel = ModelTransfer.combinationContent(dto, getQuestionCategoryNames(dto.getQuestionId()));
            //获取题干、选项、答案
            QuestionModel questionModel = qtiService.getQuestion(dto.getQuestionInnerTypeId(), true, dto.getContentXml());

            questionImportModel.setPrompt(titleString + CommonFunction.formatToHtml(questionModel.getI2()));

            if ((questionModel.getI3() == ItemType.SINGLECHOICE.getIndex()) || (questionModel.getI3() == ItemType.MULTIPLECHOICE.getIndex()) ||
                    (questionModel.getI3() == ItemType.JUDGEMENT.getIndex()) || (questionModel.getI3() == ItemType.ORDER.getIndex())) {
                //排序，判断，单选，多选
                //组织选项
                questionImportModel.getChoices().add(optionString + CommonFunction.getChoiceData(questionModel.getI6()));
                //组织答案
                questionImportModel.getAnswer().add(answerString + CommonFunction.getAnswerData(questionModel.getI7()));

            } else if (questionModel.getI3() == ItemType.TEXTENTRY.getIndex()) {
                //填空
                questionImportModel.getChoices().add(optionString);
                //组织填空题答案
                questionImportModel.getAnswer().add(answerString + CommonFunction.getTextEntryAnswerData(questionModel.getI6()));

            } else if (questionModel.getI3() == ItemType.EXTENDEDTEXT.getIndex()) {
                //问答题
                questionImportModel.getChoices().add(optionString);
                questionImportModel.getAnswer().add(answerString + questionModel.getI6().get(0));

            } else if (questionModel.getI3() == ItemType.MATCH.getIndex()) {
                //匹配题	match
                //组织题干
                List<String> combinationMatchPromptList = getMatchData(questionModel.getSub());
                questionImportModel.setPrompt(titleString + combinationMatchPromptList.get(0));
                //组织选项
                questionImportModel.getChoices().add(optionString + CommonFunction.getChoiceData(questionModel.getI6()));
                //组织答案
                questionImportModel.getAnswer().add(answerString + combinationMatchPromptList.get(1));

            } else if ((questionModel.getI3() == ItemType.COMPOSITE.getIndex()) || (questionModel.getI3() == ItemType.CLOZE.getIndex()) ||
                    (questionModel.getI3() == ItemType.READINGCOMPREHENSION.getIndex())) {
                // 组合题，上传，完型填空，阅读理解
                questionImportModel.getChoices().add("");
                questionImportModel.getAnswer().add("");
                questionImportModel.getSubItems().addAll(getSub(questionModel.getSub()));

            } else if (questionModel.getI3() == ItemType.UPLOAD.getIndex()) {
                questionImportModel.getChoices().add(optionString);
                questionImportModel.getAnswer().add(answerString);

            }
            models.add(questionImportModel);
        }

        return models;
    }

    //region 创建子试题

    /**
     * 创建子试题.
     *
     * @param subs the subs
     * @return the sub
     */
    private List<QuestionImportModel> getSub(List<QuestionModel> subs) {
        List<QuestionImportModel> models = new ArrayList<>();
        for (QuestionModel model : subs) {
            //组织文件内容
            QuestionImportModel questionImportModel = new QuestionImportModel();
            questionImportModel.setQuestionType("[子题型] 单选题");
            questionImportModel.setChapter("");
            questionImportModel.setQuestionCategory("");
            questionImportModel.setQuestionAnalysis("");
            questionImportModel.setDifficulty("");
            questionImportModel.setScore("");
            questionImportModel.setErrorInfo("");
            questionImportModel.setRemark("");

            questionImportModel.setPrompt("[题干] " + model.getI2());
            questionImportModel.getChoices().add("[选项] " + CommonFunction.getChoiceData(model.getI6()));
            questionImportModel.getAnswer().add("[答案] " + CommonFunction.getAnswerData(model.getI7()));

            models.add(questionImportModel);
        }
        return models;
    }
    //endregion

    //region 组织匹配题题干

    /**
     * 组织匹配题题干
     *
     * @param sub the sub
     * @return match data
     */
    private List<String> getMatchData(List<QuestionModel> sub) {
        List<String> stringList = new ArrayList<>();
        String formatString = "%s##";

        if (sub == null || sub.isEmpty()) {
            return stringList;
        }

        StringBuilder choiceStr = new StringBuilder();
        StringBuilder answerStr = new StringBuilder();

        for (int i = 0; i < sub.size(); i++) {
            choiceStr.append(String.format(formatString, sub.get(i).getI2()));
            answerStr.append(String.format(formatString, CommonFunction.getAnswerData(sub.get(i).getI7())));
        }

        choiceStr.setLength(choiceStr.length() - 2);
        answerStr.setLength(answerStr.length() - 2);

        stringList.add(choiceStr.toString());
        stringList.add(answerStr.toString());

        return stringList;
    }
    //endregion

    //endregion

    //region 组成导出压缩文件

    /**
     * 组成导出压缩文件.
     *
     * @param wordModelList the word model list
     * @return the string
     * @throws Exception the exception
     */
    private String makeZipFile(List<QuestionImportModel> wordModelList) throws PoiException, ZipException {

        //get zip file name
        String zipFileName = getZipFilePath();
        //make zip file
        ZipFile zipFile = new ZipFile(zipFileName);

        //get word file name
        String fileName = getWordFilePath();

        //删除给定文件夹下多于一天的历史记录
        FileUtils.deleteOldFiles(new File(fileName));
        //make word file
        printWord(wordModelList, fileName, zipFile);

        //add word file to zip file
        ArrayList filesToAdd = new ArrayList();
        filesToAdd.add(new File(fileName));

        ZipParameters parameters = new ZipParameters();
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
        zipFile.addFiles(filesToAdd, parameters);

        return zipFileName;
    }
    //endregion

    //region写入文档

    //region 获得导出的zip文件名称路径

    /**
     * 获得导出的zip文件名称路径.
     *
     * @return the file
     */
    private String getZipFilePath() {
        //导出文件名称
        String fileName = String.format("试题包-%s.zip", DateUtil.getCurrentTimeToLongString());

        return CommonFunction.getFileFullPath("package", fileName);
    }
    //endregion

    //region 获得导出的文件名称路径

    /**
     * 获得导出的word文件名称路径.
     *
     * @return the file
     */
    private String getWordFilePath() {
        //导出文件名称
        String fileName = String.format("%s.docx", DateUtil.getCurrentTimeToLongString());

        return CommonFunction.getFileFullPath(exportString, fileName);
    }
    //endregion

    //region 输出word

    /**
     * 输出word.
     *
     * @param contentToWordList the content to word list
     * @param fileName          the file name
     * @param zipFile           the zip file
     * @throws Exception the exception
     */
    private void printWord(List<QuestionImportModel> contentToWordList, String fileName, ZipFile zipFile)
            throws PoiException {

        try (CustomXWPFDocument doc = new CustomXWPFDocument()) {
            XWPFParagraph paragraph = doc.createParagraph();
            paragraph.setAlignment(ParagraphAlignment.LEFT);//文字对齐方式
            paragraph.setVerticalAlignment(TextAlignment.TOP);

            for (QuestionImportModel questionImportModel : contentToWordList) {
                processQuestion(questionImportModel, false, doc, zipFile);

                if (!questionImportModel.getSubItems().isEmpty()) {
                    for (QuestionImportModel sub : questionImportModel.getSubItems()) {
                        processQuestion(sub, true, doc, zipFile);
                    }
                }
            }

            FileOutputStream out = new FileOutputStream(fileName);
            doc.write(out);
            out.close();
        } catch (Exception e) {
            throw new PoiException(e.getMessage());
        }
    }
    //endregion

    //region 处理单个试题

    /**
     * 处理单个试题.
     *
     * @param questionImportModel the question import model
     * @param isSub               the is sub
     * @param doc                 the doc
     * @param zipFile             the zip file
     */
    private void processQuestion(QuestionImportModel questionImportModel, boolean isSub, CustomXWPFDocument doc,
                                 ZipFile zipFile) throws ZipException, IOException {
        XWPFParagraph paragraph = doc.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        //得到不含有sub部分的map，key为内容，value表示是否包含富文本
        Map<String, Boolean> dataMap = ModelTransfer.getDataMap(questionImportModel, isSub);

        for (Map.Entry<String, Boolean> map : dataMap.entrySet()) {
            //无需处理特殊标记的
            if (!map.getValue()) {
                writeNormalContent(map.getKey(), paragraph.createRun());
            } else {
                //prompt和choices需要匹配img,table,mp3等标记数据
                processAttachment(map.getKey(), doc, zipFile);
            }
        }
    }
    //endregion

    //region 输出普通内容信息

    /**
     * 输出普通内容信息.
     *
     * @param content   the content
     */
    private void writeNormalContent(String content, XWPFRun run) {
        run.setText(CommonFunction.processeHTML(content));
        if(content.contains("[") && content.contains("]")){
            run.addBreak();
        }
        run.setFontFamily(fontFamily);//字体类型
        run.setFontSize(fontSize);//字体大小
    }
    //endregion

    //region 处理附件

    /**
     * 处理附件.
     *
     * @param content   the content
     * @param doc       the doc
     * @param zipFile   the zip file
     */
    private void processAttachment(String content, CustomXWPFDocument doc, ZipFile zipFile) throws ZipException, IOException {
        XWPFParagraph paragraph = doc.createParagraph();
        paragraph.setAlignment(ParagraphAlignment.LEFT);
        //得到所有的内容
        List<String> nonAttachment = Function.splitByPattern(content);
        for (String item : nonAttachment) {
            XWPFRun run = paragraph.createRun();
            if (item.contains("<img")) {
                writeImage(Function.findPath(item), run);
            } else if (item.contains("<media") || item.contains("<video")) {
                writeMedia(Function.findPath(item), run, zipFile);
            } else if (item.contains("<table")) {
                run = paragraph.createRun();
                List<List<List<String>>> lists = CommonFunction.getTableContent(item);
                for (int j = 0; j < lists.size(); j++) {
                    writeTable(run, lists.get(j));
                }
                // 添加Table后另起一行
                paragraph = doc.createParagraph();
            } else {
                writeNormalContent(item, run);
            }
        }
    }
    //endregion

    //region 将文件添加入Zip

    /**
     * 将文件添加入Zip.
     *
     * @param file    文件有效的绝对路径
     * @param zipFile the zip file
     * @throws ZipException the zip exception
     */
    private void addFileToZip(String file, ZipFile zipFile) throws ZipException {
        ArrayList filesToAdd = new ArrayList();
        filesToAdd.add(new File(file));

        ZipParameters parameters = new ZipParameters();
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

        parameters.setRootFolderInZip("audios/");

        zipFile.addFiles(filesToAdd, parameters);
    }
    //endregion

    //region 输出图片内容

    /**
     * 输出图片内容
     *
     * @param filePath the content
     */
    private void writeImage(String filePath, XWPFRun run) throws IOException {
        //认为图片文件存在放网络路径上，并非本地路径
        String imgFile = downloadImgFileAndGetFullPath(filePath);
        createPic(run, imgFile);
    }
    //endregion

    //region 输出音频内容

    /**
     * 输出音频内容
     *
     * @param filePath  the content
     * @param zipFile   the zip file
     */
    private void writeMedia(String filePath, XWPFRun run, ZipFile zipFile) throws ZipException, IOException {
        //认为音频文件存在放网络路径上，并非本地路径
        writeNormalContent(String.format("[音频文件：%s]", downloadAudioFileAndGetFileName(filePath, zipFile)), run);

    }
    //endregion

    //region 下载远端音频文件并且保存到zip文件，返回音频文件名

    /**
     * 下载远端音频文件并且保存到zip文件，返回音频文件名.
     *
     * @param urlPath 音频文件url
     * @param zipFile zip文件
     * @return 音频文件名 string
     * @throws ZipException the zip exception
     * @throws IOException  the io exception
     */
    public String downloadAudioFileAndGetFileName(String urlPath, ZipFile zipFile) throws ZipException, IOException {
        //文件名称
        String fileName = String.format("%s.mp3", DateUtil.getCurrentTimeToLongString());
        //本地保存路径
        String filePath = CommonFunction.getFileFullPath(exportString, fileName);
        //下载并保存文件
        FileUtils.writeUrlFileToLocal(urlPath, filePath);
        //写入zip
        addFileToZip(filePath, zipFile);

        return fileName;
    }

    //endregion

    //region 下载远端图片文件并且，返回文件路径

    /**
     * 下载远端图片文件并且，返回文件路径.
     *
     * @param urlPath 图片文件url
     * @return 图片文件路径 string
     * @throws IOException the io exception
     */
    public String downloadImgFileAndGetFullPath(String urlPath) throws IOException {
        //文件名称
        String fileName = Function.getImgFileName(urlPath);
        //本地保存路径
        String filePath = CommonFunction.getFileFullPath(exportString, fileName);
        //下载并保存文件
        FileUtils.writeUrlFileToLocal(urlPath, filePath);

        return filePath;
    }

    //endregion

    //region 将图片文件输出到文档

    /**
     * 将图片文件输出到文档.
     *
     * @param imgFile the image file
     * @throws InvalidFormatException the invalid format exception
     */
    private void createPic(XWPFRun run, String imgFile) throws IOException {
        CustomXWPFDocument doc = (CustomXWPFDocument) run.getParagraph().getDocument();
        try {
            byte[] imageByte = FileUtils.image2byte(imgFile);

            int formatType = XWPFDocument.PICTURE_TYPE_PNG;

            if (imgFile.toLowerCase().lastIndexOf(".png") < 0) {
                formatType = XWPFDocument.PICTURE_TYPE_JPEG;
            }

            doc.addPictureData(imageByte, formatType);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
        }

        File picture = new File(imgFile);
        BufferedImage sourceImg = ImageIO.read(new FileInputStream(picture));

        int newWidth = 300;
        int newHeight = 0;
        if (sourceImg.getWidth() < newWidth) {
            newWidth = sourceImg.getWidth();
            newHeight = sourceImg.getHeight();
        } else {
            newHeight = sourceImg.getHeight() / (sourceImg.getWidth() / newWidth);
        }

        doc.createPicture(run.getParagraph(), doc.getAllPictures().size() - 1, newWidth, newHeight, "");
    }
    //endregion

    //region 根据表格内容输出word表格

    /**
     * 根据表格内容输出word表格.
     *
     * @param tableContent the table content
     */
    private void writeTable(XWPFRun run, List<List<String>> tableContent) {
        if (tableContent == null || tableContent.isEmpty() || tableContent.get(0).isEmpty()) {
            return;
        }

        CustomXWPFDocument doc = (CustomXWPFDocument) run.getParagraph().getDocument();
        XWPFTable xwpfTable = doc.createTable(tableContent.size(), tableContent.get(0).size());
        List<XWPFTableRow> xwpfTableRows = xwpfTable.getRows();
        XWPFTableRow row;
        List<XWPFTableCell> cells;
        XWPFTableCell cell;

        int rowSize = xwpfTableRows.size();
        int cellSize;

        for (int i = 0; i < rowSize; i++) {
            row = xwpfTableRows.get(i);
            cells = row.getTableCells();
            cellSize = cells.size();
            for (int j = 0; j < cellSize; j++) {
                cell = cells.get(j);
                cell.setText(tableContent.get(i).get(j));
            }
        }
    }
    //endregion

    //endregion

    //endregion

    //region 获取试题模型

    /**
     * 获取试题模型
     *
     * @param itemBankId 题库ID
     * @return
     */
    @Override
    public ReturnModel getQuestionModel(String itemBankId) {
        ReturnModel model = new ReturnModel();

        //合法性
        CourseItemBank itemBank = itemBankService.getCourseItemBankInfo(itemBankId);
        if (itemBank == null) {
            model.setCode(GlobalConst.COURSE_NOT_EXIST);
            model.setMessage(itembankIdNotExist);
            return model;
        }
        QuestionModel questionModel = new QuestionModel();

        model.setData(questionModel);
        model.setCode(GlobalConst.SUCCESS);
        return model;
    }

    //endregion

}
